package org.xx.armory.jpa;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.transaction.annotation.Transactional;
import org.xx.armory.domain.Book;
import org.xx.armory.spring5.PageArgs;

import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import static org.springframework.beans.BeanUtils.copyProperties;

public class BookProviderImpl
        implements BookProvider {
    @Autowired
    private BookRepository bookRepository;

    private Specification<BookPO> findAllSpecification(
            String title,
            String author,
            Date startDate,
            Date endDate
    ) {
        return (root, cq, cb) ->
                new PredicateBuilder()
                        .add(() -> !title.isEmpty(), cb.like(root.get(BookPO_.title), "%" + title + "%"))
                        .add(() -> !author.isEmpty(), cb.like(root.get(BookPO_.author), "%" + author + "%"))
                        .add(() -> startDate != null, cb.greaterThanOrEqualTo(root.get(BookPO_.pubDate), startDate))
                        .add(() -> endDate != null, cb.lessThan(root.get(BookPO_.pubDate), endDate))
                        .build(cb);

    }

    @Override
    @Transactional
    public Optional<Book> save(
            Book book
    ) {
        final BookPO bookPO = new BookPO();
        copyProperties(book, bookPO);
        return Optional.ofNullable(this.bookRepository.saveAndFlush(bookPO)).map(this::toBook);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Book> findAll(
            String title,
            String author,
            Date startDate,
            Date endDate,
            int startRowIndex,
            int maximumRows
    ) {
        return this.bookRepository.findAll(
                this.findAllSpecification(title, author, startDate, endDate),
                PageArgs.of(startRowIndex, maximumRows)
        )
                                  .map(this::toBook)
                                  .filter(Objects::nonNull)
                                  .get()
                                  .collect(Collectors.toList());
    }

    @Override
    @Transactional(readOnly = true)
    public int count(
            String title,
            String author,
            Date startDate,
            Date endDate
    ) {
        return (int) this.bookRepository.count(this.findAllSpecification(title, author, startDate, endDate));
    }

    private Book toBook(
            BookPO po
    ) {
        if (po == null) {
            return null;
        }

        return new Book(
                po.getId(),
                po.getTitle(),
                po.getAuthor(),
                po.getPubDate(),
                po.getPrice()
        );
    }
}
